This document contains a description of optimisation tools for electric system planing simulation. If you are only interested in operation you might prefer to go back to file optim-Operation.ipynb. In files BasicFunctionalities/input-XXX.ipynb you can learn to understand input data (consumption, availability).
(Text below is almost the same as for operation)
This document will gives a chance to understand
It proposes to enter the subject by increasing progressively the number of variables and constraints in the optimisation problem, hence moving toward more realism through the document, introducing:
It relies on different test cases that allow to
If, after reading this file, you want to build your own pyomo model you can go to optim-Planing-Advanced.ipynb.
#region importation of modules
import os
if os.path.basename(os.getcwd())=="BasicFunctionalities":
os.chdir('..') ## to work at project root like in any IDE
import sys
if sys.platform != 'win32':
myhost = os.uname()[1]
else : myhost = ""
if (myhost=="jupyter-sop"):
## for https://jupyter-sop.mines-paristech.fr/ users, you need to
# (1) run the following in a terminal
if (os.system("/opt/mosek/9.2/tools/platform/linux64x86/bin/lmgrd -c /opt/mosek/9.2/tools/platform/linux64x86/bin/mosek.lic -l lmgrd.log")==0):
os.system("/opt/mosek/9.2/tools/platform/linux64x86/bin/lmutil lmstat -c 27007@127.0.0.1 -a")
# (2) definition of license
os.environ["MOSEKLM_LICENSE_FILE"] = '@jupyter-sop'
import numpy as np
import pandas as pd
import csv
#import docplex
import datetime
import copy
import plotly.graph_objects as go
import matplotlib.pyplot as plt
from sklearn import linear_model
import sys
from functions.f_planingModels import *
from functions.f_optimization import *
from functions.f_graphicalTools import *
# Change this if you have other solvers obtained here
## https://ampl.com/products/solvers/open-source/
## for eduction this site provides also several professional solvers, that are more efficient than e.g. cbc
#endregion
#region Solver and data location definition
InputFolder='Data/input/'
solver= 'mosek' ## no need for solverpath with mosek.
BaseSolverPath='/Users/robin.girard/Documents/Code/Packages/solvers/ampl_macosx64'
sys.path.append(BaseSolverPath)
solvers= ['gurobi','knitro','cbc'] # 'glpk' is too slow 'cplex' and 'xpress' do not work
solverpath= {}
for solver in solvers : solverpath[solver]=BaseSolverPath+'/'+solver
cplexPATH='/Applications/CPLEX_Studio1210/cplex/bin/x86-64_osx'
sys.path.append(cplexPATH)
solverpath['cplex']=cplexPATH+"/"+"cplex"
solver = 'mosek'
#endregion
Before you start with the math, you should
#region I - Simple single area : loading parameters
Zones="FR" ; year=2013
#### reading areaConsumption availabilityFactor and TechParameters CSV files
areaConsumption = pd.read_csv(InputFolder+'areaConsumption'+str(year)+'_'+str(Zones)+'.csv',sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP"])
availabilityFactor = pd.read_csv(InputFolder+'availabilityFactor'+str(year)+'_'+str(Zones)+'.csv',sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP","TECHNOLOGIES"])
TechParameters = pd.read_csv(InputFolder+'Planing-Simple_TECHNOLOGIES.csv',sep=',',decimal='.',skiprows=0,comment="#").set_index(["TECHNOLOGIES"])
#### Selection of subset
Selected_TECHNOLOGIES=['OldNuke','CCG'] #you can add technologies here
availabilityFactor=availabilityFactor.loc[(slice(None),Selected_TECHNOLOGIES),:]
TechParameters=TechParameters.loc[Selected_TECHNOLOGIES,:]
#TechParameters.loc[TechParameters.TECHNOLOGIES=="OldNuke",'maxCapacity']=63000 ## Limit to actual installed capacity
#endregion
#region I - Simple single area : Solving and loading results
model = GetElectricSystemModel_PlaningSingleNode(areaConsumption,availabilityFactor,TechParameters)
if solver in solverpath : opt = SolverFactory(solver,executable=solverpath[solver])
else : opt = SolverFactory(solver)
results=opt.solve(model)
## result analysis
Variables=getVariables_panda_indexed(model)
print(extractCosts(Variables))
print(extractEnergyCapacity(Variables))
#pour avoir la production en KWh de chaque moyen de prod chaque heure
production_df=Variables['energy'].pivot(index="TIMESTAMP",columns='TECHNOLOGIES', values='energy')
### Check sum Prod = Consumption
Delta=(production_df.sum(axis=1) - areaConsumption.areaConsumption);
abs(Delta).max()
#endregion
Capacity_Milliards_euros Energy_Milliards_euros
TECHNOLOGIES
OldNuke 19.060436 3.308393
CCG 2.628352 1.333826
Capacity_GW Production_TWh
TECHNOLOGIES
OldNuke 77.778651 472.627621
CCG 22.485684 19.386999
5.367473931983113e-08
Verify that the sum of market prices allows all actors to cover fixed and marginal cost. do they earn more ? why ?
#region I - Simple single area : visualisation and lagrange multipliers
### representation des résultats
TIMESTAMP_d=pd.date_range(start=str(year)+"-01-01 00:00:00",end=str(year)+"-12-31 23:00:00", freq="1H")
production_df.index=TIMESTAMP_d; areaConsumption.index=TIMESTAMP_d;
fig=MyStackedPlotly(y_df=production_df,Conso = areaConsumption)
fig=fig.update_layout(title_text="Production électrique (en KWh)", xaxis_title="heures de l'année")
#plotly.offline.plot(fig, filename='file.html') ## offline
fig.show()
#### lagrange multipliers
Constraints= getConstraintsDual_panda(model)
# Analyse energyCtr
energyCtrDual=Constraints['energyCtr']; energyCtrDual['energyCtr']=energyCtrDual['energyCtr']
energyCtrDual
round(energyCtrDual.energyCtr,2).unique()
# Analyse CapacityCtr
CapacityCtrDual=Constraints['CapacityCtr'].pivot(index="TIMESTAMP",columns='TECHNOLOGIES', values='CapacityCtr')*1000000;
round(CapacityCtrDual,2)
round(CapacityCtrDual.OldNuke,2).unique() ## if you increase by Delta the installed capacity of nuke you decrease by xxx the cost when nuke is not sufficient
round(CapacityCtrDual.CCG,2).unique() ## increasing the capacity of CCG as no effect on prices
#endregion
array([-0.0000e+00, -1.1689e+11])
In the this section, we will increase the complexity of the problem given in Section 2 and add : dependency on area z (country), a congestion constraint, ramp constraints.
\begin{align} &\text{Cost function }& &\min_{x} \sum_z \left ( \beta_{iz}\bar{x_{iz}}+ \sum_t \sum_i \pi_{iz} x_{itz}\;\;\; & & \pi_{iz}\right ) \text{ marginal cost}\\ &\text{Power limit } & &\text{ s.t.} \;\; 0 \leq x_{itz}\leq a_{itz} \bar{x_{iz}} & &\bar{x_{iz}} \text{ installed power, } a_{itz} \text{ availability}\\ &\text{Meet demand } & & \sum_i x_{itz} \geq C_{tz} && C_{tz} \text{ Consumption}\\ &\text{Stock limit } & &\sum_t x_{it}\leq E_i && E_i=\bar{x_i}*N_i \text{ Energy capacity limit}\\ &\text{ramp limit } & &rc^-_i *x_{it}\leq x_{it}-x_{i(t+1)}\leq rc^+_i *x_{it} && rc^+_i rc^-_i\text{ ramp limit}\\ \end{align}#region II - Ramp Single area : loading parameters loading parameterscase with ramp constraints
Zones="FR"
year=2013
Selected_TECHNOLOGIES=['OldNuke', 'CCG',"curtailment"] #you'll add 'Solar' after
#### reading CSV files
areaConsumption = pd.read_csv(InputFolder+'areaConsumption'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP"])
availabilityFactor = pd.read_csv(InputFolder+'availabilityFactor'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP","TECHNOLOGIES"])
TechParameters = pd.read_csv(InputFolder+'Planing-RAMP1BIS_TECHNOLOGIES.csv',sep=',',decimal='.',skiprows=0,comment="#").set_index(["TECHNOLOGIES"])
#### Selection of subset
availabilityFactor=availabilityFactor.loc[(slice(None),Selected_TECHNOLOGIES),:]
TechParameters=TechParameters.loc[Selected_TECHNOLOGIES,:]
TechParameters.loc["OldNuke",'RampConstraintMoins']=0.01 ## a bit strong to put in light the effect
TechParameters.loc["OldNuke",'RampConstraintPlus']=0.02 ## a bit strong to put in light the effect
#endregion
#region II - Ramp Single area : solving and loading results
model = GetElectricSystemModel_PlaningSingleNode(areaConsumption,availabilityFactor,TechParameters)
opt = SolverFactory(solver)
results=opt.solve(model)
Variables=getVariables_panda_indexed(model)
print(extractCosts(Variables))
print(extractEnergyCapacity(Variables))
#pour avoir la production en KWh de chaque moyen de prod chaque heure
production_df=Variables['energy'].pivot(index="TIMESTAMP",columns='TECHNOLOGIES', values='energy')
### Check sum Prod = Consumption
Delta=(production_df.sum(axis=1) - areaConsumption.areaConsumption);
abs(Delta).max()
#endregion
Capacity_Milliards_euros Energy_Milliards_euros
TECHNOLOGIES
OldNuke 19.576265 3.431494
CCG 1.974773 1.562460
curtailment 0.000000 0.139293
Capacity_GW Production_TWh
TECHNOLOGIES
OldNuke 79.883561 490.213440
CCG 16.894287 22.710175
curtailment 1000.000000 0.046431
13312.986334695714
#region II - Ramp Single area : visualisation and lagrange multipliers
TIMESTAMP_d=pd.date_range(start=str(year)+"-01-01 00:00:00",end=str(year)+"-12-31 23:00:00", freq="1H")
production_df.index=TIMESTAMP_d; areaConsumption.index=TIMESTAMP_d;
fig=MyStackedPlotly(y_df=production_df,Conso = areaConsumption)
fig=fig.update_layout(title_text="Production électrique (en KWh)", xaxis_title="heures de l'année")
#plotly.offline.plot(fig, filename='file.html') ## offline
fig.show()
#### lagrange multipliers
Constraints= getConstraintsDual_panda(model)
# Analyse energyCtr
energyCtrDual=Constraints['energyCtr']; energyCtrDual['energyCtr']=energyCtrDual['energyCtr']*1000000
energyCtrDual
round(energyCtrDual.energyCtr,2).unique()
# Analyse CapacityCtr
CapacityCtrDual=Constraints['CapacityCtr'].pivot(index="TIMESTAMP",columns='TECHNOLOGIES', values='CapacityCtr')*1000000;
round(CapacityCtrDual,2)
round(CapacityCtrDual.OldNuke,2).unique() ## if you increase by Delta the installed capacity of nuke you decrease by xxx the cost when nuke is not sufficient
round(CapacityCtrDual.CCG,2).unique() ## increasing the capacity of CCG as no effect on prices
#endregion
array([-0.00000000e+00, -7.23624262e+06, -2.25720000e+09, -2.93120000e+09,
-2.74580000e+09, -1.98200000e+09, -2.16860000e+09, -2.72480000e+09,
-2.79360000e+09, -2.80060000e+09, -2.58720000e+09, -2.05080000e+09])
Math here are in 3.1
#region III - Ramp multiple area : loading parameters
Zones="FR_DE_GB_ES"
year=2016
Selected_AREAS=["FR","DE"]
Selected_TECHNOLOGIES=['OldNuke', 'CCG','WindOnShore',"curtailment"] #you'll add 'Solar' after #'NewNuke', 'HydroRiver', 'HydroReservoir','WindOnShore', 'WindOffShore', 'Solar', 'Curtailement'}
#### reading CSV files
areaConsumption = pd.read_csv(InputFolder+'areaConsumption'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["AREAS","TIMESTAMP"])
availabilityFactor = pd.read_csv(InputFolder+'availabilityFactor'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["AREAS","TIMESTAMP","TECHNOLOGIES"])
TechParameters = pd.read_csv(InputFolder+'Planing_MultiNode_DE-FR_TECHNOLOGIES_AREAS.csv',
sep=',',decimal='.',skiprows=0,comment="#").set_index(["AREAS","TECHNOLOGIES"])
ExchangeParameters = pd.read_csv(InputFolder+'Hypothese_DE-FR_AREAS_AREAS.csv',sep=',',decimal='.',skiprows=0,comment="#").set_index(["AREAS","AREAS.1"])
#### Selection of subset
TechParameters=TechParameters.loc[(Selected_AREAS,Selected_TECHNOLOGIES),:]
areaConsumption=areaConsumption.loc[(Selected_AREAS,slice(None)),:]
availabilityFactor=availabilityFactor.loc[(Selected_AREAS,slice(None),Selected_TECHNOLOGIES),:]
TechParameters.loc[(slice(None),"OldNuke"),'RampConstraintMoins']=0.01 ## a bit strong to put in light the effect
TechParameters.loc[(slice(None),"OldNuke"),'RampConstraintPlus']=0.02 ## a bit strong to put in light the effect
#endregion
#region III - Ramp multiple area : solving and loading results
### small data cleaning
availabilityFactor.availabilityFactor[availabilityFactor.availabilityFactor>1]=1
model = GetElectricSystemModel_PlaningMultiNode(areaConsumption,availabilityFactor,TechParameters,ExchangeParameters)
opt = SolverFactory(solver)
results=opt.solve(model)
Variables=getVariables_panda(model)
print(extractCosts(Variables))
print(extractEnergyCapacity(Variables))
production_df=EnergyAndExchange2Prod(Variables)
### Check sum Prod = Consumption
Delta= production_df.sum(axis=1)-areaConsumption.areaConsumption
abs(Delta).sum()
## adding dates
production_df_=ChangeTIMESTAMP2Dates(production_df,year)
areaConsumption_=ChangeTIMESTAMP2Dates(areaConsumption,year)
fig=MyAreaStackedPlot(production_df_,Conso=areaConsumption_)
fig=fig.update_layout(title_text="Production électrique (en KWh)", xaxis_title="heures de l'année")
plotly.offline.plot(fig, filename='file.html') ## offline
Constraints= getConstraintsDual_panda(model)
Constraints.keys()
#endregion
Capacity_Milliards_euros Energy_Milliards_euros
AREAS TECHNOLOGIES
FR OldNuke 19.237361 3.198864
WindOnShore 1.784651 0.000000
CCG 1.265871 1.361098
curtailment 0.000000 0.069163
DE OldNuke 15.743431 2.591443
WindOnShore 6.360066 0.000000
CCG 2.953698 3.377376
curtailment 0.000000 -0.000000
Capacity_GW Production_TWh
AREAS TECHNOLOGIES
FR OldNuke 78.286132 456.980563
WindOnShore 13.410000 22.677167
CCG 10.800000 19.783406
curtailment 1000.000000 0.023054
DE OldNuke 64.067642 370.206139
WindOnShore 47.790000 62.397707
CCG 25.200000 49.089770
curtailment 1000.000000 0.000000
dict_keys(['energyCostsDef', 'capacityCostsCtr', 'CapacityCtr', 'exchangeCtrPlus', 'exchangeCtrMoins', 'exchangeCtr2', 'energyCtr', 'maxCapacityCtr', 'minCapacityCtr', 'storageCtr', 'rampCtrPlus', 'rampCtrMoins', 'rampCtrPlus2', 'rampCtrMoins2'])
Just have a look at optim-Storage.ipynb
This section is a bit more difficult that the other one. You can skip the math if you're only interested in using the storage optimisation tool.
#region IV Ramp+Storage single area : loading parameters
Zones="FR"
year=2013
Selected_TECHNOLOGIES=['OldNuke','WindOnShore', 'CCG',"curtailment"] ## try adding 'HydroRiver', 'HydroReservoir'
#### reading CSV files
areaConsumption = pd.read_csv(InputFolder+'areaConsumption'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP"])
availabilityFactor = pd.read_csv(InputFolder+'availabilityFactor'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP","TECHNOLOGIES"])
TechParameters = pd.read_csv(InputFolder+'Planing-RAMP1BIS_TECHNOLOGIES.csv',
sep=',',decimal='.',skiprows=0,comment="#").set_index(["TECHNOLOGIES"])
#### Selection of subset
availabilityFactor=availabilityFactor.loc[(slice(None),Selected_TECHNOLOGIES),:]
TechParameters=TechParameters.loc[Selected_TECHNOLOGIES,:]
#TechParameters.loc["CCG",'capacity']=100000 ## margin to make everything work
TechParameters.loc["OldNuke",'RampConstraintMoins']=0.02 ## a bit strong to put in light the effect
TechParameters.loc["OldNuke",'RampConstraintPlus']=0.02 ## a bit strong to put in light the effect
p_max=5000 ### 7 GW de STEP + 10 GW de batteries
StorageParameters={"p_max" : p_max , "c_max": p_max*10,"efficiency_in": 0.9,"efficiency_out" : 0.9}
#endregion
#region IV Ramp+Storage single area : solving and loading results
res= GetElectricSystemModel_PlaningSingleNode_with1Storage(areaConsumption,availabilityFactor,
TechParameters,StorageParameters)
Variables = getVariables_panda_indexed(res['model'])
Constraints = getConstraintsDual_panda(res['model'])
areaConsumption = res["areaConsumption"]
print(extractCosts(Variables))
print(extractEnergyCapacity(Variables))
production_df=Variables['energy'].pivot(index="TIMESTAMP",columns='TECHNOLOGIES', values='energy')
Delta= production_df.sum(axis=1)-areaConsumption["NewConsumption"]
print(sum(abs(Delta)))
production_df.loc[:,'Storage'] = -areaConsumption["Storage"] ### put storage in the production time series
production_df.sum(axis=0)/10**6 ### energies produites TWh
production_df[production_df>0].sum(axis=0)/10**6 ### energies produites TWh
production_df.max(axis=0)/1000 ### Pmax en GW
TIMESTAMP_d=pd.date_range(start=str(year)+"-01-01 00:00:00",end=str(year)+"-12-31 23:00:00", freq="1H")
production_df.index=TIMESTAMP_d; areaConsumption.index=TIMESTAMP_d;
fig=MyStackedPlotly(y_df=production_df, Conso=areaConsumption)
fig=fig.update_layout(title_text="Production électrique (en KWh)", xaxis_title="heures de l'année")
#plotly.offline.plot(fig, filename='file.html') ## offline
fig.show()
stats=res["stats"]
#endregion
0
1
2
Capacity_Milliards_euros Energy_Milliards_euros
TECHNOLOGIES
OldNuke 18.899795 3.275479
WindOnShore 1.779775 0.000000
CCG 1.903598 1.540048
curtailment 0.000000 0.143950
Capacity_GW Production_TWh
TECHNOLOGIES
OldNuke 77.123134 467.925563
WindOnShore 13.410000 14.596632
CCG 16.285376 22.384425
curtailment 1000.000000 0.047983
9091837.875880724
Case V is in section 5. (lots of renewable)
#region VI Case Storage + CCG + Nuke (Ramp+Storage single area) : loading parameters
Zones="FR"
year=2013
Selected_TECHNOLOGIES=['CCG', 'NewNuke',"curtailment",'HydroRiver', 'HydroReservoir']
#### reading CSV files
areaConsumption = pd.read_csv(InputFolder+'areaConsumption'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP"])
availabilityFactor = pd.read_csv(InputFolder+'availabilityFactor'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP","TECHNOLOGIES"])
TechParameters = pd.read_csv(InputFolder+'Planing-RAMP1BIS_TECHNOLOGIES.csv',
sep=',',decimal='.',skiprows=0,comment="#").set_index(["TECHNOLOGIES"])
#### Selection of subset
availabilityFactor=availabilityFactor.loc[(slice(None),Selected_TECHNOLOGIES),:]
TechParameters=TechParameters.loc[Selected_TECHNOLOGIES,:]
TechParameters.loc["CCG",'energyCost']=300
TechParameters.loc["CCG",'RampConstraintMoins']=0.05 ## a bit strong to put in light the effect
TechParameters.loc["CCG",'RampConstraintPlus']=0.05 ## a bit strong to put in light the effect
TechParameters.loc["NewNuke",'RampConstraintMoins']=0.01 ## a bit strong to put in light the effect
TechParameters.loc["NewNuke",'RampConstraintPlus']=0.02 ## a bit strong to put in light the effect
p_max=10000
StorageParameters={"p_max" : p_max , "c_max": p_max*10,"efficiency_in": 0.9,"efficiency_out" : 0.9}
#endregion
#region VI Case Storage + CCG + Nuke (Ramp+Storage single area) : solving and loading results
res= GetElectricSystemModel_PlaningSingleNode_with1Storage(areaConsumption,availabilityFactor,
TechParameters,StorageParameters)
Variables = getVariables_panda_indexed(res['model'])
Constraints = getConstraintsDual_panda(res['model'])
areaConsumption = res["areaConsumption"]
print(extractCosts(Variables))
print(extractEnergyCapacity(Variables))
production_df=Variables['energy'].pivot(index="TIMESTAMP",columns='TECHNOLOGIES', values='energy')
Delta= production_df.sum(axis=1)-areaConsumption["NewConsumption"]
print(sum(abs(Delta)))
production_df.loc[:,'Storage'] = -areaConsumption["Storage"] ### put storage in the production time series
production_df.sum(axis=0)/10**6 ### energies produites TWh
production_df[production_df>0].sum(axis=0)/10**6 ### energies produites TWh
production_df.max(axis=0)/1000 ### Pmax en GW
TIMESTAMP_d=pd.date_range(start=str(year)+"-01-01 00:00:00",end=str(year)+"-12-31 23:00:00", freq="1H")
production_df.index=TIMESTAMP_d; areaConsumption.index=TIMESTAMP_d;
fig=MyStackedPlotly(y_df=production_df, Conso=areaConsumption)
fig=fig.update_layout(title_text="Production électrique (en KWh)", xaxis_title="heures de l'année")
#plotly.offline.plot(fig, filename='file.html') ## offline
fig.show()
stats=res["stats"]
#endregion
0
1
2
Capacity_Milliards_euros Energy_Milliards_euros
TECHNOLOGIES
NewNuke 25.684493 3.173119
HydroRiver 0.807500 0.000000
CCG 1.262412 1.228994
HydroReservoir 0.565250 0.000000
curtailment 0.000000 0.287678
Capacity_GW Production_TWh
TECHNOLOGIES
NewNuke 65.118 453.302725
HydroRiver 10.000 33.854268
CCG 10.800 4.096646
HydroReservoir 7.000 14.700000
curtailment 1000.000 0.095893
7164886.894218007
#region VI Ramp+Storage Multi area : loading parameters
Zones="FR_DE_GB_ES"
year=2016
Selected_AREAS=["FR","DE"]
Selected_TECHNOLOGIES=['OldNuke', 'CCG','WindOnShore',"curtailment"] #you'll add 'Solar' after #'NewNuke', 'HydroRiver', 'HydroReservoir','WindOnShore', 'WindOffShore', 'Solar', 'Curtailement'}
#### reading CSV files
TechParameters = pd.read_csv(InputFolder+'Planing_MultiNode_DE-FR_TECHNOLOGIES_AREAS.csv',
sep=',',decimal='.',comment="#").set_index(["AREAS","TECHNOLOGIES"])
areaConsumption = pd.read_csv(InputFolder+'areaConsumption'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["AREAS","TIMESTAMP"])
availabilityFactor = pd.read_csv(InputFolder+'availabilityFactor'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["AREAS","TIMESTAMP","TECHNOLOGIES"])
ExchangeParameters = pd.read_csv(InputFolder+'Hypothese_DE-FR_AREAS_AREAS.csv',
sep=',',decimal='.',skiprows=0,comment="#").set_index(["AREAS","AREAS.1"])
#### Selection of subset
TechParameters=TechParameters.loc[(Selected_AREAS,Selected_TECHNOLOGIES),:]
areaConsumption=areaConsumption.loc[(Selected_AREAS,slice(None)),:]
availabilityFactor=availabilityFactor.loc[(Selected_AREAS,slice(None),Selected_TECHNOLOGIES),:]
TechParameters.loc[(slice(None),'CCG'),'energyCost']=300 ## margin to make everything work
TechParameters.loc[(slice(None),"OldNuke"),'RampConstraintMoins']=0.01 ## a bit strong to put in light the effect
TechParameters.loc[(slice(None),"OldNuke"),'RampConstraintPlus']=0.02 ## a bit strong to put in light the effect
p_max=10000
StorageParameters=pd.DataFrame([])
for AREA in Selected_AREAS :
StorageParameters_ = {"AREA": AREA, "p_max": p_max, "c_max": p_max * 10, "efficiency_in": 0.9,
"efficiency_out": 0.9}
StorageParameters=StorageParameters.append(pd.DataFrame([StorageParameters_]))
StorageParameters=StorageParameters.set_index("AREA")
#endregion
#region VI Ramp+Storage multi area : solving and loading results
res= GetElectricSystemModel_PlaningMultiNode_with1Storage(areaConsumption,availabilityFactor,
TechParameters,ExchangeParameters,StorageParameters)
Variables = getVariables_panda(res['model'])
production_df=EnergyAndExchange2Prod(Variables)
areaConsumption = res["areaConsumption"]
#Variables['energy'].loc['Storage'] = areaConsumption["Storage"]
Delta=(production_df.sum(axis=1) - areaConsumption.NewConsumption); ## comparaison à la conso incluant le stockage
abs(Delta).max()
production_df.loc[:,'Storage'] = -areaConsumption["Storage"] #### ajout du stockage comme production
Delta=(production_df.sum(axis=1) - areaConsumption.areaConsumption);
abs(Delta).max()
production_df_=ChangeTIMESTAMP2Dates(production_df,year)
areaConsumption_=ChangeTIMESTAMP2Dates(areaConsumption,year)
fig=MyAreaStackedPlot(production_df_,Conso=areaConsumption_)
fig=fig.update_layout(title_text="Production électrique (en KWh)", xaxis_title="heures de l'année")
#plotly.offline.plot(fig, filename='file.html') ## offline
fig.show()
abs(areaConsumption["Storage"]).groupby(by="AREAS").sum() ## stockage
production_df.groupby(by="AREAS").sum()/10**6 ### energies produites TWh
production_df[production_df>0].groupby(by="AREAS").sum()/10**6 ### energies produites TWh
production_df.groupby(by="AREAS").max()/1000 ### Pmax en GW ### le stockage ne fait rien en Allemagne ??? bizarre
production_df.groupby(by="AREAS").min()/1000 ### Pmax en GW
#endregion
0 1 2
| CCG | OldNuke | WindOnShore | curtailment | DE | FR | Storage | |
|---|---|---|---|---|---|---|---|
| AREAS | |||||||
| DE | -1.335820e-15 | 20.967332 | 0.0 | 0.0 | 0.0 | -9.0 | -9.0 |
| FR | -3.363994e-13 | 34.785339 | 0.0 | 0.0 | -9.0 | 0.0 | -9.0 |
#region V Case Storage + CCG + PV + Wind + hydro (Ramp+Storage single area) : loading parameters
Zones="FR"
year=2013
Selected_TECHNOLOGIES=['CCG', 'WindOnShore','Solar',"curtailment",'HydroRiver', 'HydroReservoir']
#### reading CSV files
areaConsumption = pd.read_csv(InputFolder+'areaConsumption'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP"])
availabilityFactor = pd.read_csv(InputFolder+'availabilityFactor'+str(year)+'_'+str(Zones)+'.csv',
sep=',',decimal='.',skiprows=0).set_index(["TIMESTAMP","TECHNOLOGIES"])
TechParameters = pd.read_csv(InputFolder+'Planing-RAMP1BIS_TECHNOLOGIES.csv',
sep=',',decimal='.',skiprows=0,comment="#").set_index(["TECHNOLOGIES"])
#### Selection of subset
availabilityFactor=availabilityFactor.loc[(slice(None),Selected_TECHNOLOGIES),:]
TechParameters=TechParameters.loc[Selected_TECHNOLOGIES,:]
#TechParameters.loc["CCG",'capacity']=100000 ## margin to make everything work
TechParameters.loc["CCG",'energyCost']=300
TechParameters.loc["CCG",'RampConstraintMoins']=0.05 ## a bit strong to put in light the effect
TechParameters.loc["CCG",'RampConstraintPlus']=0.05 ## a bit strong to put in light the effect
p_max=17000 ## storage capacity is not optimized 7 GW de STEP + 10 GW batteries
StorageParameters={"p_max" : p_max , "c_max": p_max*10,"efficiency_in": 0.9,"efficiency_out" : 0.9}
#endregion
#region V Case Storage + CCG + PV + Wind + hydro (Ramp+Storage single area) : solving and loading results
res= GetElectricSystemModel_PlaningSingleNode_with1Storage(areaConsumption,availabilityFactor,
TechParameters,StorageParameters)
Variables = getVariables_panda_indexed(res['model'])
Constraints = getConstraintsDual_panda(res['model'])
areaConsumption = res["areaConsumption"]
print(extractCosts(Variables))
print(extractEnergyCapacity(Variables))
production_df=Variables['energy'].pivot(index="TIMESTAMP",columns='TECHNOLOGIES', values='energy')
Delta= production_df.sum(axis=1)-areaConsumption["NewConsumption"]
print(sum(abs(Delta)))
production_df.loc[:,'Storage'] = -areaConsumption["Storage"] ### put storage in the production time series
production_df.sum(axis=0)/10**6 ### energies produites TWh
production_df[production_df>0].sum(axis=0)/10**6 ### energies produites TWh
production_df.max(axis=0)/1000 ### Pmax en GW
TIMESTAMP_d=pd.date_range(start=str(year)+"-01-01 00:00:00",end=str(year)+"-12-31 23:00:00", freq="1H")
production_df.index=TIMESTAMP_d; areaConsumption.index=TIMESTAMP_d;
fig=MyStackedPlotly(y_df=production_df, Conso=areaConsumption)
fig=fig.update_layout(title_text="Production électrique (en KWh)", xaxis_title="heures de l'année")
#plotly.offline.plot(fig, filename='file.html') ## offline
fig.show()
stats=res["stats"]
#endregion
0
1
2
Capacity_Milliards_euros Energy_Milliards_euros
TECHNOLOGIES
WindOnShore 34.032601 0.000000
Solar 5.696089 0.000000
HydroRiver 0.807500 0.000000
CCG 6.575342 18.929286
HydroReservoir 0.565250 0.000000
curtailment 0.000000 0.648428
Capacity_GW Production_TWh
TECHNOLOGIES
WindOnShore 256.424057 522.901904
Solar 70.776450 52.274903
HydroRiver 10.000000 13.134644
CCG 56.252388 63.097619
HydroReservoir 7.000000 14.700000
curtailment 1000.000000 0.216143
168098465.4960389
TODO here : add electrolysers